﻿using System;
using System.Linq;
using System.Xml;
using System.Net;
using System.IO;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using Microsoft.Practices.Prism.Regions;
using Microsoft.Practices.Prism.Events;
using Microsoft.Practices.Prism.Commands;
using Microsoft.Practices.Unity;
using DXTools.UI.Core;
using DXTools.UI.Modules.Configuration.Views;
using DXTools.UI.Core.Events;
using System.Collections.Generic;
using DXTools.UI.Core.ViewModel;
using System.Collections.ObjectModel;
using System.Text;
using DXTools.UI.Core.DataProviders;
using DXTools.Core.CrmSdk;
using DXTools.Core.Model;
using DXTools.UI.Core.Model;
using DXTools.Core.Enum;
using DXTools.UI.Modules.Configuration.Model;
using DXTools.UI.Modules.Configuration.Events;
using System.Xml.Linq;


namespace DXTools.UI.Modules.Configuration.ViewModels
{
    public class CatalogViewModel : ViewModelBase
    {
        #region Private Attributes
        private const string HEADER_INFO = "Catalog";
        private ICRMDataProvider crmDataProvider;
        #endregion

        #region Properties

        private ObservableCollection<Tool> _toolList;
        public ObservableCollection<Tool> ToolList
        {
            get
            {
                return _toolList;
            }
            set
            {
                _toolList = value;
                RaisePropertyChanged("ToolList");
            }
        }

        private IOrderedEnumerable<Tool> _filteredToolList;
        public IOrderedEnumerable<Tool> FilteredToolList
        {
            get
            {
                return _filteredToolList;
            }
            set
            {
                _filteredToolList = value;
                RaisePropertyChanged("FilteredToolList");
            }
        }

        private String _searchText;
        public String SearchText
        {
            get
            {
                return _searchText;
            }
            set
            {
                _searchText = value;
                RaisePropertyChanged("SearchText");
            }
        }

        
        #endregion

        #region Constructors

        public CatalogViewModel(IRegionManager regionManager, IEventAggregator eventAggregator, IUnityContainer container)
            :base(regionManager,eventAggregator,container)
        {
            this.HeaderInfo = HEADER_INFO;

            this.CloseViewCommand = new DelegateCommand<object>(this.CloseView, this.CanCloseView);
            this.SearchCommand = new DelegateCommand<object>(this.Search, this.CanSearch);
            this.DownloadToolCommand = new DelegateCommand<object>(this.DownloadTool, this.CanDownloadTool);

            crmDataProvider = new CRMDataProvider();

            //eventAggregator.GetEvent<DownloadToolEvent>().Subscribe(DownloadTool);

            RetrieveCatalog();
            
        }

        #endregion

        #region Retrieve Catalog List

        private void RetrieveCatalog()
         {
             WebRequest GETRequest = null;

             AsyncCallback asyncCallback = (result =>
             {
                 Action method = () =>
                 {
                        ObservableCollection<Tool> tools = new ObservableCollection<Tool>();
                    
                        WebResponse GETResponse = GETRequest.EndGetResponse(result);
                        string uri = @"http://schemas.datacontract.org/2004/07/DXTools.Service.Configuration";

                        XDocument doc = XDocument.Load(GETResponse.GetResponseStream(), LoadOptions.SetBaseUri);

                        bool isCRMOnline = DXTools.Core.ClientScript.IsCRMOnline();

                        foreach (XElement module in doc.Root.Elements())
                        {
                            bool availableOnline = (module.Element(XName.Get("AvailableOnline", uri)) != null)? bool.Parse(module.Element(XName.Get("AvailableOnline", uri)).Value): true;
                            bool toolAvailable = false;

                            // Is this deployment CRM online or IFD and is the tool available
                            if (isCRMOnline && availableOnline == false)
                            {
                                toolAvailable = false;
                            }
                            else
                            {
                                toolAvailable = true;
                            }



                            if (toolAvailable)
                            {
                                tools.Add(new Tool()
                                {
                                    Id = (module.Element(XName.Get("Id", uri)) != null) ? Guid.Parse(module.Element(XName.Get("Id", uri)).Value) : Guid.Empty,
                                    Ref = (module.Element(XName.Get("Ref", uri)) != null) ? module.Element(XName.Get("Ref", uri)).Value : null,
                                    Name = (module.Element(XName.Get("Name", uri)) != null) ? module.Element(XName.Get("Name", uri)).Value : null,
                                    DisplayName = (module.Element(XName.Get("DisplayName", uri)) != null) ? module.Element(XName.Get("DisplayName", uri)).Value : null,
                                    ModuleName = (module.Element(XName.Get("ModuleName", uri)) != null) ? module.Element(XName.Get("ModuleName", uri)).Value : null,
                                    ModuleType = (module.Element(XName.Get("ModuleType", uri)) != null) ? module.Element(XName.Get("ModuleType", uri)).Value : null,
                                    Version = (module.Element(XName.Get("Version", uri)) != null) ? module.Element(XName.Get("Version", uri)).Value : null,
                                    AvailableOnline = availableOnline,
                                    Enable = true
                                });
                            }
                            
                        }



                     this.ToolList = tools;
                     this.FilteredToolList = from t in tools orderby t.DisplayName select t;
                     this.IsBusy = false;
                 };

                 this.View.Dispatcher.BeginInvoke(method);
             });

             GETRequest = DXTools.Core.Service.ConfigService.RetrieveModules(asyncCallback);

             
         }


        #endregion

        #region Close

         private void CloseView(object commandArg)
         {
             base.CloseView();
         }
         private bool CanCloseView(object commandArg)
         {
             return true;
         }       

        #endregion

        #region Search
        
        public DelegateCommand<object> SearchCommand { get; private set; }

        private bool CanSearch(object commandArg)
         {
             return true;
         }

        private void Search(object commandArg)
         {
             string text = Convert.ToString(commandArg);
             if (!String.IsNullOrEmpty(text))
                 SearchText = text;

             if (!String.IsNullOrEmpty(SearchText))
             {
                 this.FilteredToolList = from t 
                                         in ToolList
                                         where t.DisplayName.ToLowerInvariant().Contains(SearchText.ToLowerInvariant())
                                         orderby t.DisplayName 
                                         select t;
             }
             else
                 this.FilteredToolList = from t in ToolList orderby t.DisplayName select t;


           
         }
              

        #endregion

        #region DownloadTool Command

        public DelegateCommand<object> DownloadToolCommand { get; private set; }

        private bool CanDownloadTool(object commandArg)
        {
            return true;
        }

        private void DownloadTool(object commandArg)
        {
            if (commandArg != null && commandArg is Tool)
            {
                Tool tool = (Tool) commandArg;
                DownloadAndInstallTool(tool);
            }

        }

       

        #endregion

        #region DownloadAndInstallTool
        
        private void DownloadAndInstallTool(Tool tool)
        {
            WebRequest GETRequest = null;
            this.IsBusy = true;

            Core.Dialogs.ProgressDialog dialog = new Core.Dialogs.ProgressDialog();
            dialog.Message = "Downloading - " + tool.DisplayName;
            dialog.ProgressValue = 50;
            dialog.CanClose = false;
            dialog.ShowDialog();
            
            AsyncCallback asyncCallback = (result =>
            {
                Action method = () =>
                {

                    UpdateProgressDialog(dialog, null , 100, false);

                    WebResponse GETResponse = GETRequest.EndGetResponse(result);
                    string uri = @"http://schemas.datacontract.org/2004/07/DXTools.Service.Configuration";

                    XDocument doc = XDocument.Load(GETResponse.GetResponseStream(), LoadOptions.SetBaseUri);
                    XElement module = doc.Root;
                    Tool tmp = new Tool()
                        {
                            Id = (module.Element(XName.Get("Id", uri)) != null) ? Guid.Parse(module.Element(XName.Get("Id", uri)).Value) : Guid.Empty,
                            Ref = (module.Element(XName.Get("Ref", uri)) != null) ? module.Element(XName.Get("Ref", uri)).Value : null,
                            Name = (module.Element(XName.Get("Name", uri)) != null) ? module.Element(XName.Get("Name", uri)).Value : null,
                            DisplayName = (module.Element(XName.Get("Name", uri)) != null) ? module.Element(XName.Get("Name", uri)).Value : null,
                            ModuleName = (module.Element(XName.Get("ModuleName", uri)) != null) ? module.Element(XName.Get("ModuleName", uri)).Value : null,
                            ModuleType = (module.Element(XName.Get("ModuleType", uri)) != null) ? module.Element(XName.Get("ModuleType", uri)).Value : null,
                            Version = (module.Element(XName.Get("Version", uri)) != null) ? module.Element(XName.Get("Version", uri)).Value : null,
                            AvailableOnline = (module.Element(XName.Get("AvailableOnline", uri)) != null) ? bool.Parse(module.Element(XName.Get("AvailableOnline", uri)).Value) : true,
                            SolutionFile = (module.Element(XName.Get("SolutionFile", uri)) != null) ? module.Element(XName.Get("SolutionFile", uri)).Value : null,
                            Enable = true
                        };


                    Guid importJobId = Guid.NewGuid();

                    // Import the solution
                    if(!String.IsNullOrEmpty(tmp.SolutionFile))
                    {
                        AsyncCallback asyncCallbackImport = (resultImport =>
                        {
                            Action method2 = () =>
                            {

                                //UpdateProgressDialog(dialog, null, 90, false);
                                try
                                {

                                    IOrganizationService service = (IOrganizationService)resultImport.AsyncState;
                                    Guid responseImport = service.EndCreate(resultImport);
                                    if (responseImport == Guid.Empty)
                                    {
                                        UpdateProgressDialog(dialog, "Import of Tool " + tool.DisplayName + " failed. Solution Import Failed", 100, true);
                                        this.IsBusy = false;
                                    }
                                }
                                catch (Exception ex)
                                {
                                    //UpdateProgressDialog(dialog, "Import of Tool " + tool.DisplayName + " failed. Solution Import Failed", 100, true);
                                    //this.IsBusy = false;
                                    //throw ex;
                                }
                            };
                            this.View.Dispatcher.BeginInvoke(method2);
                        });
                        
                        UpdateProgressDialog(dialog, "Importing - " + tool.DisplayName, 0, false);
                       
                        crmDataProvider.ImportSolution(tool,importJobId,tmp.SolutionFile, asyncCallbackImport);
                        //System.Threading.Thread.Sleep(5000);

                        // Begin attempting to show status updates of import job
                        ShowImportProgress(tool.DisplayName, importJobId, dialog); 
                    }
                    else
                    {
                        
                       UpdateProgressDialog(dialog, "Import of Tool " + tool.DisplayName + " failed. Solution File not available.", 100, true);
                       this.IsBusy = false;
                        //MessageBox.Show("Import of Tool " + tool.DisplayName + " failed. Solution File not available.");
                    }
                };
                
                this.View.Dispatcher.BeginInvoke(method);
            });

            GETRequest = DXTools.Core.Service.ConfigService.RetrieveModule(tool.Id.ToString(), asyncCallback);


        }

        private void ShowImportProgress(String ToolName, Guid importJobId, DXTools.UI.Core.Dialogs.ProgressDialog dialog)
        {
            if (this.IsBusy == false)
                return;

            AsyncCallback asyncCallbackValidateImport = (resultValidateImport =>
            {
                Action methodValidateImport = () =>
                {
                    IOrganizationService serviceValidateImport = (IOrganizationService)resultValidateImport.AsyncState;
                    Entity response = null;
                    try
                    {
                        response = serviceValidateImport.EndRetrieve(resultValidateImport);
                    }
                    catch (Exception ex)
                    {

                        ShowImportProgress(ToolName, importJobId, dialog);
                        return;
                    }
                    
                    ImportJob importJob = ParseImportJobData(response);

                    if (importJob.Processed == false)
                    {
                        UpdateProgressDialog(dialog, null, importJob.Progress, false);
                        ShowImportProgress(ToolName, importJobId, dialog);

                        return;
                    }
 
                    if (importJob.ImportSuccessful)
                    {
                        
                        UpdateProgressDialog(dialog, ToolName + " Installation Successful.", 100, true);
                        
                        PerformPostInstallationActions();
                    }
                    else
                    {
                        UpdateProgressDialog(dialog, ToolName + " Installation Failed.", 100, true);
                    }
                    this.IsBusy = false;

                    
                
                };
                this.View.Dispatcher.BeginInvoke(methodValidateImport);
            });

            if (importJobId != Guid.Empty)
            {
                crmDataProvider.RetrieveImportJob(importJobId, asyncCallbackValidateImport);
            }
            else
            {
               
                UpdateProgressDialog(dialog, "Import of " + ToolName + " failed. No Import Job Id found.", 100, true);
                this.IsBusy = false;
          
                //MessageBox.Show("Import of Tool failed.");
            }
             
        }


        private ImportJob ParseImportJobData(Entity ImportJobRecord)
        {
            XDocument doc = XDocument.Parse(Convert.ToString(ImportJobRecord["data"]));
            XElement data = doc.Root;

            ImportJob importJob = new ImportJob();
            double progress = 0;
            bool processed = false;

            if(data.Attribute("progress") != null)
                double.TryParse(data.Attribute("progress").Value, out progress);

            if (data.Attribute("processed") != null)
                bool.TryParse(data.Attribute("processed").Value, out processed);

            importJob.Progress = progress;
            importJob.Processed = processed;
            importJob.ImportSuccessful = false;

            bool failedImport = false;

            try
            {
                if (importJob.Processed)
                {
                    // Check each result node to see if a failure occurred
                    foreach (XElement element in doc.Descendants("result"))
                    {
                        if (element.Attribute("result") != null)
                        {
                            string result = Convert.ToString(element.Attribute("result").Value);
                            if (result == "failure")
                            {
                                importJob.ErrorText = (element.Attribute("errortext") != null) ? Convert.ToString(element.Attribute("errortext").Value) : String.Empty;
                                importJob.ErrorCode = (element.Attribute("errorcode") != null) ? Convert.ToString(element.Attribute("errorcode").Value) : String.Empty;
                                failedImport = true;
                            }
                        }
                    } 

                    // Did the import fail
                    if(failedImport == false)
                    {
                        importJob.ImportSuccessful = true;
                    }

                    //importJob.result = (data.Element("solutionManifests").Element("solutionManifest").Element("result").Attribute("result") != null) ? Convert.ToString(data.Element("solutionManifests").Element("solutionManifest").Element("result").Attribute("result").Value) : null;
                }
            }
            catch { importJob.Processed = true; }


            return importJob;
        }

        private void UpdateProgressDialog(DXTools.UI.Core.Dialogs.ProgressDialog dialog, String Message, double ProgressValue, bool CanClose)
        {
            Action methodUpdateProgressDialog = () =>
            {
                dialog.ProgressValue = ProgressValue;
                if(Message != null)
                    dialog.Message = Message;
                dialog.CanClose = CanClose;
                dialog.UpdateLayout();
            };
            this.View.Dispatcher.BeginInvoke(methodUpdateProgressDialog);

        }

        private void PerformPostInstallationActions()
        {

           
        }

        #endregion


    }
}
